﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Duckbill
{
    public class Map
    {
        public List<Room> rooms;
        public List<Room> openRooms; //rooms that can have a new room attached
        public Dictionary<KeyValuePair<int, int>, Room> usedRooms =
            new Dictionary<KeyValuePair<int,int>,Room>();

        public Random rand = new Random();

        public Map()
        {
            int nRooms = 250;
            rooms = new List<Room>(nRooms);
            openRooms = new List<Room>(nRooms);

            AddRoom(new Room(0, 0, 1, 1));
            for (int i = 1; i < nRooms; i++)
            {
                AddNewRoom();
                LoadingState.percent = (float)(i)/nRooms;
            }
        }

        public bool IsOpen(Room room)
        {
            for (int i = room.X; i < room.X+room.W; i++)
                for (int j = room.Y; j < room.Y+room.H; j++)
                    for (int k = 0; k < 4; k++)
                        if (!usedRooms.ContainsKey(new KeyValuePair<int, int>(i+k/2, j+k%2)))
                            return true;
            return false;
        }

        public void AddNewRoom()
        {
            bool done = false;
            while (!done)
            {
                //Room neighbor = rooms[rand.Next(rooms.Count)];
                Room neighbor = openRooms[rand.Next(openRooms.Count)];
                if (!IsOpen(neighbor))
                {
                    openRooms.Remove(neighbor);
                    continue;
                }
                int dir = rand.Next(4); //direction to place new room
                int oX = neighbor.X, oY = neighbor.Y; //old position
                int nX = oX, nY = oY; //new position
                int nW = 1, nH = 1;

                int roomType = rand.Next(8);
                if (roomType < 4) //normal room
                    roomType = 0;
                else if (roomType < 6) //corridor
                    roomType = 1;
                else //small room
                    roomType = 2;

                switch (roomType)
                {
                    case 0: //normal room
                        nW = rand.Next(2, rand.Next(3, rand.Next(4, 6)));
                        nH = rand.Next(2, rand.Next(3, rand.Next(4, 6)));
                        break;
                    case 1: //corridor
                        if (rand.Next(2) == 0)
                            nW = rand.Next(4, rand.Next(6, 9));
                        else
                            nH = rand.Next(4, rand.Next(6, 9));
                        break;
                    case 2: //small room
                        break;
                }

                switch (dir)
                {
                    case 0: //left
                        oX = neighbor.X;
                        nX = oX - nW;
                        oY = neighbor.Y + rand.Next(neighbor.H);
                        nY = oY - rand.Next(nH);
                        break;
                    case 1: //right
                        oX = neighbor.X + neighbor.W - 1;
                        nX = oX + 1;
                        oY = neighbor.Y + rand.Next(neighbor.H);
                        nY = oY - rand.Next(nH);
                        break;
                    case 2: //up
                        oX = neighbor.X + rand.Next(neighbor.W);
                        nX = oX - rand.Next(nW);
                        oY = neighbor.Y;
                        nY = oY - nH;
                        break;
                    case 3: //down
                        oX = neighbor.X + rand.Next(neighbor.W);
                        nX = oX - rand.Next(nW);
                        oY = neighbor.Y + neighbor.H - 1;
                        nY = oY + 1;
                        break;
                }

                //FIXME: spatial consistency
                bool valid = true;
                for (int i = 0; i < nW; i++)
                    for (int j = 0; j < nH; j++)
                        if (usedRooms.ContainsKey(
                            new KeyValuePair<int, int>(nX+i, nY+j)))
                            valid = false;

                if (valid)
                {
                    Room newRoom = new Room(nX, nY, nW, nH);
                    AddRoom(newRoom);
                    AddDoor(newRoom, neighbor, dir, dir<2 ? oY : oX);
                    newRoom.neighbors.Add(neighbor);
                    neighbor.neighbors.Add(newRoom);
                    done = true;
                }
            }
        }
        public void AddRoom(Room room)
        {
            //TODO: interesting random rooms
            for (int i = 0; i < room.W; i++)
                for (int j = 0; j < room.H; j++)
                    usedRooms[new KeyValuePair<int, int>(room.X+i, room.Y+j)] = room;
            rooms.Add(room);
            if (IsOpen(room))
                openRooms.Add(room);
        }
        public void AddDoor(Room room1, Room room2, int dir, int otherDir)
        {
            Rectangle t1 = Rectangle.Empty, t2 = Rectangle.Empty;
            Vector2 r1 = Vector2.Zero, r2 = Vector2.Zero;
            int screenW1 = Game.SCREEN_WIDTH/room1.spriteW;
            int screenH1 = Game.SCREEN_HEIGHT/room1.spriteH;
            int screenW2 = Game.SCREEN_WIDTH/room2.spriteW;
            int screenH2 = Game.SCREEN_HEIGHT/room2.spriteH;
            int oD1 = 0, oD2 = 0;
            if (dir < 2) //otherDir is Y
            {
                oD1 = otherDir - room1.Y;
                oD2 = otherDir - room2.Y;
            }
            else //otherDir is X
            {
                oD1 = otherDir - room1.X;
                oD2 = otherDir - room2.X;
            }
            switch (dir)
            {
                case 0: //right
                    for (int i = 0; i < 4; i++)
                        room1.intTiles[screenW1*room1.W-1, 10+i + screenH1*oD1] =
                            room2.intTiles[0, 10+i + screenH2*oD2] = 0;
                    t1 = new Rectangle(screenW1*room1.W, 10 + screenH1*oD1, 1, 4);
                    r1 = new Vector2(screenW1*room1.W - 1, 10 + screenH1*oD1);
                    t2 = new Rectangle(-1, 10 + screenH2*oD2, 1, 4);
                    r2 = new Vector2(0, 10 + screenH2*oD2);
                    break;
                case 1: //left
                    for (int i = 0; i < 4; i++)
                        room1.intTiles[0, 10+i + screenH1*oD1] =
                            room2.intTiles[screenW2*room2.W-1,
                            10+i + screenH2*oD2] = 0;
                    t1 = new Rectangle(-1, 10 + screenH1*oD1, 1, 4);
                    r1 = new Vector2(0, 10 + screenH1*oD1);
                    t2 = new Rectangle(screenW2*room2.W, 10 + screenH2*oD2, 1, 4);
                    r2 = new Vector2(screenW2*room2.W - 1, 10 + screenH2*oD2);
                    break;
                case 2: //up
                    for (int i = 0; i < 4; i++)
                        room1.intTiles[8+i + screenW1*oD1, screenH1*room1.H - 1] =
                            room2.intTiles[8+i + screenW2*oD2, 0] = 0;
                    t1 = new Rectangle(8 + screenW1*oD1, screenH1*room1.H, 4, 1);
                    r1 = new Vector2(8 + screenW1*oD1, screenH1*room1.H - 2);
                    t2 = new Rectangle(8 + screenW2*oD2, -1, 4, 1);
                    r2 = new Vector2(8 + screenW2*oD2, 0);
                    break;
                case 3: //down
                    for (int i = 0; i < 4; i++)
                        room1.intTiles[8+i + screenW1*oD1, 0] =
                            room2.intTiles[8+i + screenW2*oD2,
                            screenH2*room2.H - 1] = 0;
                    t1 = new Rectangle(8 + screenW1*oD1, -1, 4, 1);
                    r1 = new Vector2(8 + screenW1*oD1, 0);
                    t2 = new Rectangle(8 + screenW2*oD2, screenH2*room2.H, 4, 1);
                    r2 = new Vector2(8 + screenW2*oD2, screenH2*room2.H - 2);
                    break;
            }
            AddTransition(room1, t1, r1, room2, t2, r2, dir<2);
            room1.SetTiles(room1.intTiles);
            room2.SetTiles(room2.intTiles);
        }

        public Room GetRoom(int i)
        {
            return rooms[i];
        }
        public void AddTransition(Room R1, Rectangle region1, Vector2 return1,
            Room R2, Rectangle region2, Vector2 return2, bool horiz)
        {
            int x1 = region1.Left*R1.spriteW, y1 = region1.Top*R1.spriteH,
                w1 = region1.Width*R1.spriteW, h1 = region1.Height*R1.spriteH,
                x2 = region2.Left*R2.spriteW, y2 = region2.Top*R2.spriteH,
                w2 = region2.Width*R2.spriteW, h2 = region2.Height*R2.spriteH;
            int rX1 = (int)return1.X*R1.spriteW, rY1 = (int)return1.Y*R1.spriteH,
                rX2 = (int)return2.X*R2.spriteW, rY2 = (int)return2.Y*R2.spriteH;
            //rX1 = x1; rY1 = y1;
            //rX2 = x2; rY2 = y2;
            TransitionType t1 = TransitionType.leftToRight,
                t2 = TransitionType.rightToLeft;
            if (horiz)
            {
                if (R1.X > R2.X)
                {
                    t1 = TransitionType.rightToLeft;
                    t2 = TransitionType.leftToRight;
                }
                else
                {
                    t1 = TransitionType.leftToRight;
                    t2 = TransitionType.rightToLeft;
                }
            }
            else
            {
                if (R1.Y < R2.Y)
                {
                    t1 = TransitionType.topToBottom;
                    t2 = TransitionType.bottomToTop;
                }
                else
                {
                    t1 = TransitionType.bottomToTop;
                    t2 = TransitionType.topToBottom;
                }
            }
            R1.AddTransition(new RoomTransition(new Vector2(x1, y1),
                new Vector2(w1, h1), R2, new Vector2(rX2, rY2), t1));
            R2.AddTransition(new RoomTransition(new Vector2(x2, y2),
                new Vector2(w2, h2), R1, new Vector2(rX1, rY1), t2));
        }
    }
}
